// Tencent is pleased to support the open source community by making Mars available.
// Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.

// Licensed under the MIT License (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://opensource.org/licenses/MIT

// Unless required by applicable law or agreed to in writing, software distributed under the License is
// distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions and
// limitations under the License.

/*
 Author		: yerungui
 Created on	: 2016-04-14
 */

#ifndef STRING_CAST_H_
#define STRING_CAST_H_

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#ifndef _WIN32
#define __STDC_FORMAT_MACROS
#include <strings.h>
#else

#endif
#include <inttypes.h>
#include <string.h>

#include <limits>
#include <string>

#include "strutil.h"

template <typename T>
char* string_cast_itoa(const T& value, char* result, uint8_t base = 10, bool upper_case = true) {
    if (!(2 <= base && base <= 36)) {
#if defined(_WIN32)
        strcpy_s(result, strlen(result), "itoa err");
#else
        strcpy(result, "itoa err");
#endif
        return result;
    }

    char *ptr_right = result, *ptr_left = result;
    T tmp_value = value;
    const char* num_mapping;

    if (upper_case)
        num_mapping = "ZYXWVUTSRQPONMLKJIHGFEDCBA9876543210123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    else
        num_mapping = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz";

    do {
        T quotient = tmp_value / base;
        *(ptr_right++) = num_mapping[35 + tmp_value - quotient * base];
        tmp_value = quotient;
    } while (tmp_value);

#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits"
#endif
    if (value < 0)
        *(ptr_right++) = '-';
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif

    *(ptr_right--) = '\0';

    while (ptr_left < ptr_right) {
        char tmp_char = *ptr_right;
        *(ptr_right--) = *ptr_left;
        *(ptr_left++) = tmp_char;
    }
    return result;
}

#define string_cast_hex(value) string_cast(value, 16)
#define string_cast_oct(value) string_cast(value, 8)

class string_cast {
 public:
    string_cast(char _value) : value_(NULL) {
        value_cache_[0] = _value;
        value_cache_[1] = '\0';
        value_ = value_cache_;
    }

    string_cast(int _value, uint8_t base = 10) : value_(NULL) {
        string_cast_itoa(_value, value_cache_, base);
        value_ = value_cache_;
    }
    string_cast(long _value, uint8_t base = 10) : value_(NULL) {
        string_cast_itoa(_value, value_cache_, base);
        value_ = value_cache_;
    }
    string_cast(long long _value, uint8_t base = 10) : value_(NULL) {
        string_cast_itoa(_value, value_cache_, base);
        value_ = value_cache_;
    }

    string_cast(unsigned int _value, uint8_t base = 10) : value_(NULL) {
        string_cast_itoa(_value, value_cache_, base);
        value_ = value_cache_;
    }
    string_cast(unsigned long _value, uint8_t base = 10) : value_(NULL) {
        string_cast_itoa(_value, value_cache_, base);
        value_ = value_cache_;
    }
    string_cast(unsigned long long _value, uint8_t base = 10) : value_(NULL) {
        string_cast_itoa(_value, value_cache_, base);
        value_ = value_cache_;
    }

    string_cast(float _value) : value_(NULL) {
        snprintf(value_cache_, sizeof(value_cache_), "%.2f", _value);
        value_ = value_cache_;
    }
    string_cast(double _value) : value_(NULL) {
        snprintf(value_cache_, sizeof(value_cache_), "%.2f", _value);
        value_ = value_cache_;
    }
    string_cast(long double _value) : value_(NULL) {
        snprintf(value_cache_, sizeof(value_cache_), "%.2Lf", _value);
        value_ = value_cache_;
    }

    string_cast(bool _value) : value_(NULL) {
        if (_value)
            value_ = "true";
        else
            value_ = "false";
        value_cache_[0] = '\0';
    }
    string_cast(const void* _value) : value_(NULL) {
        value_cache_[0] = '0';
        value_cache_[1] = 'x';
        string_cast_itoa((uintptr_t)_value, value_cache_ + 2, 16);
        value_ = value_cache_;
    }

    string_cast(const char* _value) : value_(NULL) {
        value_ = (const char*)_value;
        value_cache_[0] = '\0';
    }
    string_cast(const std::string& _value) : value_(NULL) {
        value_ = _value.c_str();
        value_cache_[0] = '\0';
    }

    const char* str() const {
        return value_;
    }
    operator const char*() const {
        return value_;
    }

 private:
    string_cast(const string_cast&);
    string_cast& operator=(const string_cast&);

 private:
    const char* value_;
    char value_cache_[65];
};

namespace detail {

template <typename T, int base = 0>
class __signed_number_cast {
 public:
    __signed_number_cast(const char* _str) : value_(0), vaild_(false) {
        if (_str == NULL)
            return;

        char* end = NULL;

        vaild_ = true;
        value_ = strtoimax(_str, &end, base);

        if (_str == end) {
            vaild_ = false;
            return;
        }

        if (value_ < (std::numeric_limits<T>::min)()) {
            value_ = (std::numeric_limits<T>::min)();
            vaild_ = false;
            return;
        }
        if ((std::numeric_limits<T>::max)() < value_) {
            value_ = (std::numeric_limits<T>::max)();
            vaild_ = false;
            return;
        }
    }

    operator T() const {
        return static_cast<T>(value_);
    }
    bool valid() const {
        return vaild_;
    }

 private:
    intmax_t value_;
    bool vaild_;
};

template <typename T, int base = 0>
class __unsigned_number_cast {
 public:
    __unsigned_number_cast(const char* _str) : value_(0), vaild_(false) {
        if (_str == NULL)
            return;

        char* end = NULL;

        vaild_ = true;
        value_ = strtoumax(_str, &end, base);

        if (_str == end) {
            vaild_ = false;
            return;
        }
        if (value_ < (std::numeric_limits<T>::min)()) {
            value_ = (std::numeric_limits<T>::min)();
            vaild_ = false;
            return;
        }
        if ((std::numeric_limits<T>::max)() < value_) {
            value_ = (std::numeric_limits<T>::max)();
            vaild_ = false;
            return;
        }
    }

    operator T() const {
        return static_cast<T>(value_);
    }
    bool valid() const {
        return vaild_;
    }

 private:
    uintmax_t value_;
    bool vaild_;
};

template <typename T>
class __float_number_cast {
 public:
    __float_number_cast(const char* _str) : value_(0), vaild_(false) {
        if (_str == NULL)
            return;

        char* end = NULL;

        vaild_ = true;
        value_ = strtod(_str, &end);

        if (_str == end) {
            vaild_ = false;
        }
    }

    operator T() const {
        return static_cast<T>(value_);
    }
    bool valid() const {
        return vaild_;
    }

 private:
    double value_;
    bool vaild_;
};
}  // namespace detail

template <typename T>
class number_cast {};

template <>
class number_cast<int8_t> : public detail::__signed_number_cast<int8_t> {
 public:
    number_cast(const char* _str) : __signed_number_cast(_str){};
};
template <>
class number_cast<int16_t> : public detail::__signed_number_cast<int16_t> {
 public:
    number_cast(const char* _str) : __signed_number_cast(_str){};
};
template <>
class number_cast<int32_t> : public detail::__signed_number_cast<int32_t> {
 public:
    number_cast(const char* _str) : __signed_number_cast(_str){};
};
template <>
class number_cast<long> : public detail::__signed_number_cast<long> {
 public:
    number_cast(const char* _str) : __signed_number_cast(_str){};
};
template <>
class number_cast<long long> : public detail::__signed_number_cast<long long> {
 public:
    number_cast(const char* _str) : __signed_number_cast(_str){};
};

template <>
class number_cast<uint8_t> : public detail::__unsigned_number_cast<uint8_t> {
 public:
    number_cast(const char* _str) : __unsigned_number_cast(_str){};
};
template <>
class number_cast<uint16_t> : public detail::__unsigned_number_cast<uint16_t> {
 public:
    number_cast(const char* _str) : __unsigned_number_cast(_str){};
};
template <>
class number_cast<uint32_t> : public detail::__unsigned_number_cast<uint32_t> {
 public:
    number_cast(const char* _str) : __unsigned_number_cast(_str){};
};
template <>
class number_cast<unsigned long> : public detail::__unsigned_number_cast<unsigned long> {
 public:
    number_cast(const char* _str) : __unsigned_number_cast(_str){};
};
template <>
class number_cast<unsigned long long> : public detail::__unsigned_number_cast<unsigned long long> {
 public:
    number_cast(const char* _str) : __unsigned_number_cast(_str){};
};

template <>
class number_cast<float> : public detail::__float_number_cast<float> {
 public:
    number_cast(const char* _str) : __float_number_cast(_str){};
};
template <>
class number_cast<double> : public detail::__float_number_cast<double> {
 public:
    number_cast(const char* _str) : __float_number_cast(_str){};
};

template <>
class number_cast<const char*> {
 public:
    number_cast(const char* _str) : value_(NULL), vaild_(false) {
        if (_str == NULL)
            return;

        value_ = _str;
        vaild_ = true;
    }

    operator const char*() const {
        return value_;
    }
    bool valid() const {
        return vaild_;
    }

 private:
    const char* value_;
    bool vaild_;
};

namespace mars {
namespace comm {
inline int StrCaseCmp(const char* ch1, const char* ch2) {
#ifdef _WIN32
    return _stricmp(ch1, ch2);
#else
    return strcasecmp(ch1, ch2);
#endif  // _WIN32
}
}
}

template <>
class number_cast<bool> {
 public:
    number_cast(const char* _str) : value_(false), vaild_(false) {
        if (_str == NULL)
            return;

        std::vector<std::string> vec_split;
        strutil::SplitToken(_str, strutil::default_delimiters<std::string>::value(), vec_split);

        if (vec_split.empty()) {
            return;
        }
        if (vec_split[0] == "1" || 0 == mars::comm::StrCaseCmp("true", vec_split[0].c_str())) {
            vaild_ = true;
            value_ = true;
        }
        if (vec_split[0] == "0" || 0 == mars::comm::StrCaseCmp("false", vec_split[0].c_str())) {
            vaild_ = true;
            value_ = false;
        }
    }

    operator bool() const {
        return value_;
    }
    bool valid() const {
        return vaild_;
    }

 private:
    bool value_;
    bool vaild_;
};

#endif /* STRING_CAST_H_ */
